package titt.server.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Socket;

import titt.io.IOFactory;
import titt.io.socket.TITTSocket;
import titt.server.os.ServerValueConfig;

public class PipedSocket extends Socket {

	private boolean isClosed = false;
	
	private PipeOut pout;
	private PipeIn pin;
	private PipedSocket s = null;
	
	public PipedSocket() throws IOException {
		pin = new PipeIn();
		
		PipeOut out = new PipeOut(pin);
		PipeIn in = new PipeIn();

		s = new PipedSocket(in, out);

		pout = new PipeOut(in);
	}
	
	public static void main(String[] args)throws Exception {
		PipedSocket p = new PipedSocket();

//		p.getPipedSocket().getOutputStream().write(1);
//		p.getInputStream().read();
		
		System.out.println("hej");
		
//		p = new PipedSocket();
		
		TITTSocket a = IOFactory.createTITTSocket(p);
		TITTSocket b = IOFactory.createTITTSocket(p.getPipedSocket());
		
		b.getOutputStream().write((byte)1);
		b.getOutputStream().flush();
		a.getInputStream().read(new byte[1]);
		
		System.out.println("hej igen");
	}
	
	private PipedSocket(PipeIn pin, PipeOut pout) {
		this.pin = pin;
		this.pout = pout;
	}
		
	@Override
	public InputStream getInputStream() throws IOException {
		return pin;
	}
	
	@Override
	public OutputStream getOutputStream() throws IOException {
		return pout;
	}
	
	@Override
	public synchronized void close() throws IOException {
		pin.close();
		pout.close();

		isClosed = true;
	}
	
	@Override
	public synchronized boolean isClosed() {
		return isClosed;
	}

	public void closePipe() throws IOException {
		close();
		if(s != null) s.close();
	}
		
	public PipedSocket getPipedSocket() {
		return s;
	}
	
	
	private class PipeIn extends PipedInputStream {
		private boolean once = true, transferHasStarted = false;

		public int read(byte[] b, int off, int len) throws IOException {
			check();
			return super.read(b, off, len);
		}
		
		public int read() throws IOException {
			check();
			return super.read();
		}
		
		private synchronized void transferStarted() {
			transferHasStarted = true;
			notifyAll();
			
		}
		
		private void check() throws IOException {
			if(once) {
				once = false;
				waitForTransfer();
			}
		}
		
		private synchronized void waitForTransfer() throws IOException {
			if(!transferHasStarted)
				try {
					wait(ServerValueConfig.get().getTimeout());
				}
				catch(Exception e) {
				}
				
			if(!transferHasStarted)
				throw new IOException("transfer initiation took too long");
		}

	}
	
	private class PipeOut extends PipedOutputStream {
		private PipeIn in;
		private boolean once = true;
		public PipeOut(PipeIn in) throws IOException {
			super(in);
			this.in = in;
		}
		public void flush() throws IOException {
			check();
			super.flush();
		}
		private void check() {			
			if(once) {
				once = false;
				in.transferStarted();
			}
		}
	}

}
